package jp.eisbahn.mgapi.api.photo;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import jp.eisbahn.mgapi.api.NopResponse;
import jp.eisbahn.mgapi.api.Request;
import jp.eisbahn.mgapi.api.Response;
import jp.eisbahn.mgapi.api.Visibility;
import jp.eisbahn.mgapi.http.ContentType;
import jp.eisbahn.mgapi.http.HttpMethod;

/**
 * The request class to access to the Photo API.
 * @author Yoichiro Tanaka
 */
public class PhotoRequest implements Request {

    /** Base URL to get the album list. */
    private static final String GET_ALBUM_LIST_BASE_URI =
        "/photo/albums/{userId}/@self";
    /** Base URL to get the album. */
    private static final String GET_ALBUM_BASE_URI =
        "/photo/albums/{userId}/@self/{albumId}";
    /** Base URL to get the photo list. */
    private static final String GET_MEDIAITEM_LIST_BASE_URI =
        "/photo/mediaItems/{userId}/@self/{albumId}";
    /** Base URL to get the photo. */
    private static final String GET_MEDIAITEM_BASE_URI =
        "/photo/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to get the comment list of the album. */
    private static final String GET_ALBUM_COMMENT_LIST_BASE_URI =
        "/photo/comments/albums/{userId}/@self/{albumId}";
    /** Base URI to get the comment list of the photo. */
    private static final String GET_PHOTO_COMMENT_LIST_BASE_URI =
        "/photo/comments/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to get the favorite list of the photo. */
    private static final String GET_PHOTO_FAVORITE_LIST_BASE_URI =
        "/photo/favorites/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to create a new album. */
    private static final String POST_ALBUM_BASE_URI =
        "/photo/albums/{userId}/@self";
    /** Base URI to delete the album. */
    private static final String DELETE_ALBUM_BASE_URI =
        "/photo/albums/{userId}/@self/{albumId}";
    /** Base URI to post a new comment for the album. */
    private static final String POST_ALBUM_COMMENT_BASE_URI =
        "/photo/comments/albums/{userId}/@self/{albumId}";
    /** Base URI to delete the comment for the album. */
    private static final String DELETE_ALBUM_COMMENT_BASE_URI =
        "/photo/comments/albums/{userId}/@self/{albumId}/{commentId}";
    /** Base URI to post a new photo. */
    private static final String POST_PHOTO_BASE_URI =
        "/photo/mediaItems/{userId}/@self/{albumId}";
    /** Base URI to delete the photo. */
    private static final String DELETE_PHOTO_BASE_URI =
        "/photo/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to post a new comment for the photo. */
    private static final String POST_PHOTO_COMMENT_BASE_URI =
        "/photo/comments/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to delete the comment from the photo. */
    private static final String DELETE_PHOTO_COMMENT_BASE_URI =
        "/photo/comments/mediaItems/{userId}/@self/{albumId}/"
      + "{mediaItemId}/{commentId}";
    /** Base URI to post a favorite for the photo. */
    private static final String POST_PHOTO_FAVORITE_BASE_URI =
        "/photo/favorites/mediaItems/{userId}/@self/{albumId}/{mediaItemId}";
    /** Base URI to delete the favorite from the photo. */
    private static final String DELETE_PHOTO_FAVORITE_BASE_URI =
        "/photo/favorites/mediaItems/{userId}/@self/{albumId}/"
      + "{mediaItemId}/{favoriteUserId}";

    /** The request type. */
    private RequestType requestType;
    /** The user ID. */
    private String userId;
    /** The album ID. */
    private String albumId;
    /** The media item ID. */
    private String mediaItemId;
    /** The access key. */
    private String accessKey;
    /** The comment ID. */
    private String commentId;
    /** The favorite user ID. */
    private String favoriteUserId;
    /** The title. */
    private String title;
    /** The description. */
    private String description;
    /** The visibility. */
    private Visibility visibility;
    /** The text. */
    private String text;
    /** The photo stream. */
    private InputStream photoStream;
    /** The image type. */
    private ImageType imageType;
    /** The header map. */
    private Map<String, String> headerMap = new HashMap<String, String>();

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getUri()
     */
    @Override
    public final String getUri() {
        if (requestType == RequestType.GET_ALBUM_LIST) {
            return GET_ALBUM_LIST_BASE_URI.replace("{userId}", userId);
        } else if (requestType == RequestType.GET_ALBUM) {
            return GET_ALBUM_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId);
        } else if (requestType == RequestType.GET_MEDIAITEM_LIST) {
            return GET_MEDIAITEM_LIST_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId);
        } else if (requestType == RequestType.GET_MEDIAITEM) {
            return GET_MEDIAITEM_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
        } else if (requestType == RequestType.GET_ALBUM_COMMENT_LIST) {
            return GET_ALBUM_COMMENT_LIST_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId);
        } else if (requestType == RequestType.GET_PHOTO_COMMENT_LIST) {
            return GET_PHOTO_COMMENT_LIST_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
        } else if (requestType == RequestType.GET_PHOTO_FAVORITE_LIST) {
            return GET_PHOTO_FAVORITE_LIST_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
        } else if (requestType == RequestType.POST_ALBUM) {
            return POST_ALBUM_BASE_URI.replace("{userId}", userId);
        } else if (requestType == RequestType.DELETE_ALBUM) {
            return DELETE_ALBUM_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId);
        } else if (requestType == RequestType.POST_ALBUM_COMMENT) {
            String url = POST_ALBUM_COMMENT_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else if (requestType == RequestType.DELETE_ALBUM_COMMENT) {
            String url = DELETE_ALBUM_COMMENT_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{commentId}", commentId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else if (requestType == RequestType.POST_PHOTO) {
            String url = POST_PHOTO_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId);
            try {
                StringBuilder sb = new StringBuilder(url);
                sb.append("?title=");
                sb.append(URLEncoder.encode(title, "UTF-8"));
                url = sb.toString();
            } catch (UnsupportedEncodingException e) {
                throw new IllegalStateException(e);
            }
            return url;
        } else if (requestType == RequestType.DELETE_PHOTO) {
            return DELETE_PHOTO_BASE_URI.replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
        } else if (requestType == RequestType.POST_PHOTO_COMMENT) {
            String url = POST_PHOTO_COMMENT_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else if (requestType == RequestType.DELETE_PHOTO_COMMENT) {
            String url = DELETE_PHOTO_COMMENT_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId)
                    .replace("{commentId}", commentId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else if (requestType == RequestType.POST_PHOTO_FAVORITE) {
            String url = POST_PHOTO_FAVORITE_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else if (requestType == RequestType.DELETE_PHOTO_FAVORITE) {
            String url = DELETE_PHOTO_FAVORITE_BASE_URI
                    .replace("{userId}", userId)
                    .replace("{albumId}", albumId)
                    .replace("{mediaItemId}", mediaItemId)
                    .replace("{favoriteUserId}", favoriteUserId);
            if (accessKey != null && accessKey.length() > 0) {
                try {
                    StringBuilder sb = new StringBuilder(url);
                    sb.append("?accessKey=");
                    sb.append(URLEncoder.encode(accessKey, "UTF-8"));
                    url = sb.toString();
                } catch (UnsupportedEncodingException e) {
                    throw new IllegalStateException(e);
                }
            }
            return url;
        } else {
            throw new IllegalStateException("RequestType not set.");
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getHttpMethod()
     */
    @Override
    public final HttpMethod getHttpMethod() {
        switch (requestType) {
            case GET_ALBUM_LIST:
            case GET_ALBUM:
            case GET_MEDIAITEM_LIST:
            case GET_MEDIAITEM:
            case GET_ALBUM_COMMENT_LIST:
            case GET_PHOTO_COMMENT_LIST:
            case GET_PHOTO_FAVORITE_LIST:
                return HttpMethod.GET;
            case POST_ALBUM:
            case POST_ALBUM_COMMENT:
            case POST_PHOTO:
            case POST_PHOTO_COMMENT:
            case POST_PHOTO_FAVORITE:
                return HttpMethod.POST;
            case DELETE_ALBUM:
            case DELETE_ALBUM_COMMENT:
            case DELETE_PHOTO:
            case DELETE_PHOTO_COMMENT:
            case DELETE_PHOTO_FAVORITE:
                return HttpMethod.DELETE;
            default:
                throw new IllegalStateException("RequestType not set.");
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getResponseClass()
     */
    @Override
    public final Class<? extends Response> getResponseClass() {
        switch(requestType) {
            case GET_ALBUM_LIST:
            case GET_ALBUM:
                return PhotoAlbumResponse.class;
            case GET_MEDIAITEM_LIST:
            case GET_MEDIAITEM:
                return PhotoPhotoResponse.class;
            case GET_ALBUM_COMMENT_LIST:
            case GET_PHOTO_COMMENT_LIST:
                return PhotoCommentResponse.class;
            case GET_PHOTO_FAVORITE_LIST:
                return PhotoUserResponse.class;
            case POST_ALBUM:
            case POST_PHOTO:
                return PhotoIdResponse.class;
            case DELETE_ALBUM:
            case POST_ALBUM_COMMENT:
            case DELETE_ALBUM_COMMENT:
            case DELETE_PHOTO:
            case POST_PHOTO_COMMENT:
            case DELETE_PHOTO_COMMENT:
            case POST_PHOTO_FAVORITE:
            case DELETE_PHOTO_FAVORITE:
                return NopResponse.class;
            default:
                throw new IllegalStateException("RequestType not set.");
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getContentType()
     */
    @Override
    public final ContentType getContentType() {
        if (requestType.equals(RequestType.POST_PHOTO)) {
            return ContentType.BINARY;
        } else {
            return ContentType.JSON;
        }
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getParameterMap()
     */
    @Override
    public final Map<String, Object> getParameterMap() {
        Map<String, Object> parameterMap = new HashMap<String, Object>();
        if (requestType == RequestType.POST_ALBUM) {
            parameterMap.put("title", title);
            parameterMap.put("description", description);
            Map<String, String> privacy = new HashMap<String, String>();
            privacy.put("visibility", visibility.toString());
            if (visibility.equals(Visibility.access_key)) {
                privacy.put("accessKey", accessKey);
            }
            parameterMap.put("privacy", privacy);
        } else if (requestType == RequestType.POST_ALBUM_COMMENT) {
            parameterMap.put("text", text);
        } else if (requestType == RequestType.POST_PHOTO_COMMENT) {
            parameterMap.put("text", text);
        }
        return parameterMap;
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getBodyStream()
     */
    @Override
    public final InputStream getBodyStream() {
        return photoStream;
    }

    /*
     * (non-Javadoc)
     * @see jp.eisbahn.mgapi.api.Request#getHeaderMap()
     */
    @Override
    public final Map<String, String> getHeaderMap() {
        return headerMap;
    }

    /**
     * Set a request type.
     * @param newRequestType The request type.
     */
    public final void setRequestType(final RequestType newRequestType) {
        requestType = newRequestType;
    }

    /**
     * Retrieve a request type.
     * @return The request type value.
     */
    public final RequestType getRequestType() {
        return requestType;
    }

    /**
     * Set a user ID.
     * @param newUserId The user ID.
     */
    public final void setUserId(final String newUserId) {
        userId = newUserId;
    }

    /**
     * Retrieve a user ID.
     * @return The user ID.
     */
    public final String getUserId() {
        return userId;
    }

    /**
     * Set an album ID.
     * @param newAlbumId The album ID.
     */
    public final void setAlbumId(final String newAlbumId) {
        albumId = newAlbumId;
    }

    /**
     * Retrieve an album ID.
     * @return The album ID.
     */
    public final String getAlbumId() {
        return albumId;
    }

    /**
     * Set a media item ID.
     * @param newMediaItemId The media item ID.
     */
    public final void setMediaItemId(final String newMediaItemId) {
        mediaItemId = newMediaItemId;
    }

    /**
     * Retrieve a media item ID.
     * @return The media item ID.
     */
    public final String getMediaItemId() {
        return mediaItemId;
    }

    /**
     * Set an access key.
     * @param newAccessKey The access key.
     */
    public final void setAccessKey(final String newAccessKey) {
        accessKey = newAccessKey;
    }

    /**
     * Retrieve an access key.
     * @return The access key.
     */
    public final String getAccessKey() {
        return accessKey;
    }

    /**
     * Set a comment ID.
     * @param newCommentId The comment ID.
     */
    public final void setCommentId(final String newCommentId) {
        commentId = newCommentId;
    }

    /**
     * Retrieve a comment ID.
     * @return The comment ID string.
     */
    public final String getCommentId() {
        return commentId;
    }

    /**
     * Set a favorite user ID.
     * @param newFavoriteUserId The favorite user ID.
     */
    public final void setFavoriteUserId(final String newFavoriteUserId) {
        favoriteUserId = newFavoriteUserId;
    }

    /**
     * Retrieve a favorite user ID.
     * @return The favorite user ID.
     */
    public final String getFavoriteUserId() {
        return favoriteUserId;
    }

    /**
     * Set a title.
     * @param newTitle The title string.
     */
    public final void setTitle(final String newTitle) {
        title = newTitle;
    }

    /**
     * Retrieve the title.
     * @return The title string.
     */
    public final String getTitle() {
        return title;
    }

    /**
     * Set a description.
     * @param newDescription The description string.
     */
    public final void setDescription(final String newDescription) {
        description = newDescription;
    }

    /**
     * Retrieve the description.
     * @return The description string.
     */
    public final String getDescription() {
        return description;
    }

    /**
     * Set a visibility value.
     * @param newVisibility The visibility value.
     */
    public final void setVisibility(final Visibility newVisibility) {
        visibility = newVisibility;
    }

    /**
     * Retrieve the visibility value.
     * @return The visibility value.
     */
    public final Visibility getVisibility() {
        return visibility;
    }

    /**
     * Set a text.
     * @param newText The text string.
     */
    public final void setText(final String newText) {
        text = newText;
    }

    /**
     * Retrieve the text.
     * @return The text string.
     */
    public final String getText() {
        return text;
    }

    /**
     * Set a photo stream.
     * @param newPhotoStream The photo stream.
     */
    public final void setPhotoStream(final InputStream newPhotoStream) {
        photoStream = newPhotoStream;
    }

    /**
     * Retrieve the photo stream.
     * @return The photo stream.
     */
    public final InputStream getPhotoStream() {
        return photoStream;
    }

    /**
     * Set a image type.
     * @param newImageType The new image type value.
     */
    public final void setImageType(final ImageType newImageType) {
        imageType = newImageType;
        if (imageType.equals(ImageType.GIF)) {
            headerMap.put("Content-Type", "image/gif");
        } else if (imageType.equals(ImageType.JPEG)) {
            headerMap.put("Content-Type", "image/jpeg");
        }
    }

    /**
     * Retrieve the image type.
     * @return The image type value.
     */
    public final ImageType getImageType() {
        return imageType;
    }

}
